using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Input;
using System.Threading;
using System.ComponentModel;
#if WINDOWS_PHONE
using Microsoft.Phone.Shell;
#endif

namespace SPGE
{
    public abstract class GameContainer : Game
    {
        public static GameContainer CurrentContainer
        { get; set; }

        public GraphicsDeviceManager DeviceManager;

        public GameTime CurrentGameTime
        { get; set; }

        public static GraphicsDevice Device
        { get { return CurrentContainer.GraphicsDevice; } }
        internal static ContentManager ContentManager
        { get { return CurrentContainer.Content; } }

        private IScene current;
        public IScene CurrentScene
        {
            get { return current; }
            protected set
            {
                current = value;
                if (CanInitialize)
                    current.Initialize();
            }
        }

        private IScene nextScene = null;
        public IScene NextScene
        {
            set
            {
                lock (nextSceneLock)
                {
                    nextScene = value;
                }
            }
            protected get
            {
                return nextScene;
            }
        }

        private object nextSceneLock = new object();
        private IScene secureNextScene = null;

        public string CurrentLoadedGroup
        { get; set; }

        bool CanLoad
        { get; set; }
        bool CanInitialize
        { get; set; }

        public ResourceGroupHandler ResourceGroups
        { get; private set; }

        public SpriteBatch SpriteBatch
        { get; set; }

        /// <summary> The BackgroundWorker item to handle async calls (used during loading).</summary>
        BackgroundWorker worker = new BackgroundWorker();

        public GameContainer(bool fullscreen)
        {
#if WINDOWS
            IsMouseVisible = true;
#endif

            DeviceManager = new GraphicsDeviceManager(this);
            DeviceManager.IsFullScreen = fullscreen;
            DeviceManager.PreferredBackBufferHeight = 800;
            DeviceManager.PreferredBackBufferWidth = 480;

            DeviceManager.SupportedOrientations = DisplayOrientation.Portrait;

            Content.RootDirectory = "Content";

            // Frame rate is 30 fps by default for Windows Phone.
            TargetElapsedTime = TimeSpan.FromTicks(333333);

            // Extend battery life under lock.
            InactiveSleepTime = TimeSpan.FromSeconds(1);

            CurrentContainer = this;
            worker.DoWork += LoadAsync;
        }

        protected abstract ResourceGroupHandler SetupGame();

        bool gameSetup = false;
        protected override void Initialize()
        {
            base.Initialize();

            if (gameSetup)
            {
                if (CurrentScene != null)
                    CurrentScene.Initialize();

                CanInitialize = true;
            }
        }

        protected override void LoadContent()
        {
#if WINDOWS_PHONE
            PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
#endif

            base.LoadContent();

            SpriteBatch = new Microsoft.Xna.Framework.Graphics.SpriteBatch(GraphicsDevice);

            CanLoad = true;

            if (!gameSetup)
            {
                gameSetup = true;

                ResourceGroups = SetupGame();

                Initialize();
            }

            ResourceGroups.LoadLoadingGroup(Content);

            if (CurrentScene != null && !CurrentScene.IsLoaded)
                ResourceGroups.LoadFirstGroupWith(CurrentScene, Content);
        }

        protected override void UnloadContent()
        {
            base.UnloadContent();

            SpriteBatch.Dispose();

            CanLoad = false;

#if WINDOWS_PHONE
            PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
#endif
        }

        protected override void Update(GameTime gameTime)
        {
            CurrentGameTime = gameTime;

            base.Update(gameTime);

            if (NextScene != null && !worker.IsBusy)
                worker.RunWorkerAsync();

            lock (nextSceneLock)
            {
                if (secureNextScene != null)
                {
                    CurrentScene = secureNextScene;
                    secureNextScene = null;
                }
            };

            if (CurrentScene != null)
            {
                if (CurrentScene is GameItemContainer)
                    (CurrentScene as GameItemContainer).BaseUpdate(gameTime);
                else
                    CurrentScene.Update(gameTime);

                if (CurrentScene.IsDone)
                    NextScene = CurrentScene.GetNextScene();
            }
        }

        private void LoadAsync(Object sender, DoWorkEventArgs e)
        {
            IScene next = null;
            lock (nextSceneLock)
            {
                next = NextScene;
                NextScene = null;
            }

            string oldGroup = CurrentScene != null ? CurrentScene.LoadGroup : null;

            if (ResourceGroups.Groups.ContainsKey(DefaultResourceGroups.LoadScene.ToString()) &&
                ResourceGroups.Groups[DefaultResourceGroups.LoadScene.ToString()].Count > 0)
            {
                var loadScene = ResourceGroups.Groups[DefaultResourceGroups.LoadScene.ToString()][0];

                lock (nextSceneLock)
                {
                    secureNextScene = loadScene;
                }
            }

            if (!ResourceGroups.IsLoaded(next) && oldGroup != null)
            {
                if (ResourceGroups.IsLoaded(oldGroup))
                    ResourceGroups.UnloadGroup(oldGroup);
            }

            if (CanLoad && !ResourceGroups.IsLoaded(next))
                ResourceGroups.LoadGroup(next.LoadGroup, Content);

            lock (nextSceneLock)
            {
                secureNextScene = next;
            }
        }

        protected override void Draw(GameTime gameTime)
        {
            base.Draw(gameTime);

            if (CurrentScene != null)
                CurrentScene.Draw(gameTime, SpriteBatch);
        }

        protected override void OnExiting(object sender, EventArgs args)
        {
            base.OnExiting(sender, args);
        }
    }
}
